using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace RpgServiceLib.Quest
{
	public delegate void ChangedQuestHandler (QuestChangeEvent questevent);
	
	public class Quest:IQuest
	{
		protected virtual string nameOfQuest { get; set; }
		protected virtual string descriptionOfQuest { get; set; }

		public virtual event ChangedQuestHandler changed;

		protected virtual int currentQuestInList  { get; set; }

		protected virtual QuestStatus statusOfQuest {get;set;}

		protected virtual QuestListType questListType { get; set; }

		protected virtual QuestType questType { get; set; }
		
		protected virtual IQuestChangeAllowedStrategy changeStrategy { get; set;}
		protected virtual IQuestStatusHandlerStrategy statusHandleStrategy { get; set;}		

		public Quest (string name, string desc, int id)
		{
			this.nameOfQuest = name;
			this.descriptionOfQuest = desc;
			this.ListOfEmbeddedQuests=new List<IQuest>();
			currentQuestInList = -1;
			this.changeStrategy = new DefaultQuestChangeAllowedStrategy();
			this.statusHandleStrategy = new DefaultQuestStatusHandleStrategy();
			this.Id = id;
		}
		
		public int Id {get;protected set;}
				

		#region IQuest implementation
		public virtual IList<IQuest> ListOfEmbeddedQuests {get; protected set;}
		
		public virtual string NameOfQuest { 
			get {
				return this.nameOfQuest;
			}
		}
		public virtual string DescriptionOfQuest { get {
			return this.descriptionOfQuest;
		  }
		}
		
		public virtual int CurrentQuestInList  { 
			get {
				return currentQuestInList;
			}
		}

		public virtual QuestStatus StatusOfQuest {
			get {
				return statusOfQuest;
			}
		}

		public virtual QuestListType QuestListType {
			get {
				return questListType;
			}
		}

		public virtual QuestType QuestType {get {
				return questType;
			}
		}

		#endregion

		#region IQuest implementation
		public virtual IQuest AddQuest (IQuest quest)
		{
			if (!this.changeStrategy.IsAllowedToAddQuest(this, quest)) 
			{			
				if (changed != null) {
					changed.Invoke (
						new QuestChangeEvent (
							this, 
							QuestChangeAllowedType.IsNotAllowed, 
							QuestChangeType.ListOfEmbeddedQuestsChanged,
							quest,
							null));
				}		       
				return this;
			}
			
			this.ListOfEmbeddedQuests.Add (quest);
			
			if (changed != null) {
				changed.Invoke(new QuestChangeEvent(this,QuestChangeType.ListOfEmbeddedQuestsChanged, this.ListOfEmbeddedQuests.Count-1, this.ListOfEmbeddedQuests.Count));
			}		       
			
			quest.LinkedFromQuest = this;
			
			
			return this;
		}

		public virtual QuestChangeEvent SetNameOfQuest (string name)
		{
			if (!this.changeStrategy.IsAllowedToChangeName (this, name)) {
				QuestChangeEvent qceNot = 
						new QuestChangeEvent (
							this, 
							QuestChangeAllowedType.IsNotAllowed, 
							QuestChangeType.NameOfQuestChanged,
							this.nameOfQuest,
							name);

				if (changed != null) {
					changed.Invoke (qceNot);
				}		       
				return qceNot;
			}

			string oldName = this.nameOfQuest;
			
			nameOfQuest = name;
			
			QuestChangeEvent qce = new QuestChangeEvent(this,QuestChangeType.NameOfQuestChanged, oldName, name);
			if (changed != null) {
				
				changed.Invoke (qce);
			}		       
			
			return qce;
		}

		public virtual QuestChangeEvent SetDescriptionOfQuest (string description)
		{
			if (!this.changeStrategy.IsAllowedToChangeDescription (this, description)) {
				QuestChangeEvent qceNot = 
						new QuestChangeEvent (
							this, 
							QuestChangeAllowedType.IsNotAllowed, 
							QuestChangeType.DescriptionOfQuestChanged,
							this.descriptionOfQuest,
							description);
				
				if (changed != null) {
					changed.Invoke (qceNot);
				}		       
				return qceNot;
			}

			string oldName = this.descriptionOfQuest;
			
			descriptionOfQuest = description;
			
			QuestChangeEvent qce = new QuestChangeEvent(this,QuestChangeType.DescriptionOfQuestChanged, oldName, description);
			if (changed != null) {
				
				changed.Invoke (qce);
			}		       
			
			return qce;
		}

		public virtual QuestChangeEvent SetQuestListType (QuestListType questListType)
		{
			if (!this.changeStrategy.IsAllowedToChangeListType (this, questListType)) {
				QuestChangeEvent qceNot = new QuestChangeEvent (
							this, 
							QuestChangeAllowedType.IsNotAllowed, 
							QuestChangeType.QuestListTypeChanged,
							this.questListType,
							questListType);
				 
				if (changed != null) {
					changed.Invoke (qceNot);
				}		       
				return qceNot;
			}
			QuestListType old = this.questListType;
			
			this.questListType = questListType;
			
			QuestChangeEvent qce = new QuestChangeEvent(this,QuestChangeType.QuestListTypeChanged, old, questListType);
			if (changed != null) {
				
				changed.Invoke (qce);
			}		       
			
			return qce;
		}

		public virtual QuestChangeEvent SetQuestStatus (QuestStatus questStatus)
		{
			if (!this.changeStrategy.IsAllowedToChangeStatus (this, questStatus)) {
				QuestChangeEvent qceNot = new QuestChangeEvent (
							this, 
							QuestChangeAllowedType.IsNotAllowed, 
							QuestChangeType.StatusOfQuestChanged,
							this.questListType,
							questListType);
				if (changed != null) {
					changed.Invoke (qceNot);
				}		       
				return qceNot;
			}
		
			QuestStatus old = this.statusOfQuest;
			
			this.statusOfQuest = questStatus;
			
			QuestChangeEvent qce = new QuestChangeEvent(this,QuestChangeType.StatusOfQuestChanged, old, questStatus);
			if (changed != null) {
				
				changed.Invoke (qce);
			}	
			
			if (old!=this.statusOfQuest)
			{
				this.statusHandleStrategy.HandleStatusChange(this, old, this.statusOfQuest);
			}	       	       
			
			return qce;
		}

		public virtual QuestChangeEvent SetQuestType (QuestType questType)
		{
			if (!this.changeStrategy.IsAllowedToChangeType(this, questType)) {
				QuestChangeEvent qceNot = new QuestChangeEvent (
							this, 
							QuestChangeAllowedType.IsNotAllowed, 
							QuestChangeType.QuestTypeChanged,
							this.questType,
							questType);

				//new QuestChangeEvent (this, QuestChangeType.NotAllowed, questType, QuestChangeType.QuestTypeChanged);
				if (changed != null) {
					changed.Invoke (qceNot);
				}		       
				return qceNot;
			}
			QuestType old = this.questType;
			
			this.questType = questType;
			
			QuestChangeEvent qce = new QuestChangeEvent(this,QuestChangeType.QuestTypeChanged, old, questType);
			if (changed != null) {
				
				changed.Invoke (qce);
			}		       
			
			return qce;
		}
		#endregion

		#region IQuest implementation
		public virtual IQuest LinkedFromQuest {
			get; set;
		}
		#endregion

		#region IQuest implementation
		public virtual IQuest SetQuestChangeAllowedStrategy (IQuestChangeAllowedStrategy questChangeAllowedStrategy)
		{
			this.changeStrategy = questChangeAllowedStrategy;
			return this;
		}

		public virtual IQuest SetQuestStatusHandlerStrategy (IQuestStatusHandlerStrategy questStatusHandlerStrategy)
		{
			this.statusHandleStrategy = questStatusHandlerStrategy;
			return this;
		}
		
		public IQuest GetQuestById (int id)
		{
			List<IQuest> liste = new List<IQuest> (from q in 
					         this.ListOfEmbeddedQuests
						where q.Id == id
						select q);
						
			foreach (var q in this.ListOfEmbeddedQuests) {
				IQuest nQuest = q.GetQuestById (id);
				if (nQuest!=null)
				{
					liste.Add(nQuest);
				}
			}
			
			if (liste.Count==1)
			{
				return liste[0];
			}
			return null;
		}

		public IEnumerable<IQuest> GetQuestByName (string name)
		{
			List<IQuest> liste = new List<IQuest>(from q in 
					         this.ListOfEmbeddedQuests
						where q.NameOfQuest == name
						select q);
						
			foreach (var q in this.ListOfEmbeddedQuests)
			{
				liste.AddRange(q.GetQuestByName(name));
			}
			
			return liste;
					     
		}		
		#endregion

	}
}

